﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Media.Media3D;
using LFSRecord2.Misc;

namespace LFSRecord2.Model
{
    public class CompLayerLookCamera : CompLayerBase
    {
        public enum PropertyTypes
        {
            Position = 0,
            LookAt = 1,
            Orientation = 2,
            Roll = 3,
            Fov = 4,
        }

        [Flags]
        public enum PropertyTypesFlags
        {
            None = 0,
            Position = 1,
            LookAt = 2,
            Orientation = 4,
            Roll = 8,
            Fov = 16,
        }

        public CompLayerLookCamera(short layerIndex, string layerName)
        {
            UniqueID = MathHelper.UniqueID;
            LayerIndex = layerIndex;
            LayerType = CompLayerTypes.LookCamera;
            LayerName = layerName;
            LayerObjectMesh = new CompLayerCameraMesh();

            IsActiveCamera = false;

            // Setup properties
            Properties = new List<CompLayerPropertyBase>(4);
            Properties.Add(new CompLayerPropertyPoint3D(this, 0, "Position", new Point3D(0, 0, 20), LayerPropertyValueMeaning.Generic, true));
            Properties.Add(new CompLayerPropertyPoint3D(this, 1, "Look At", new Point3D(0, 100, 0), LayerPropertyValueMeaning.Generic, true));
            Properties.Add(new CompLayerPropertyPoint3D(this, 2, "Orientation", new Point3D(0, 0, 0), LayerPropertyValueMeaning.Degrees, false));
            Properties.Add(new CompLayerPropertyDouble(this, 3, "Roll", 0, LayerPropertyValueMeaning.Degrees));
            Properties.Add(new CompLayerPropertyDouble(this, 4, "FOV", 60, LayerPropertyValueMeaning.Generic));

            ((CompLayerPropertyDouble)Properties[(int)PropertyTypes.Fov]).MinValue = 10;
            ((CompLayerPropertyDouble)Properties[(int)PropertyTypes.Fov]).MaxValue = 120;

            LayerObjectMesh.LookAtPoint = new Point3D(0, 100, 0);

            SetCurrentTransformMatrix();
        }

        //Point3D pPos;
        public override Matrix3D GetTransformMatrixAtFrame(int frame)
        {
            Point3D pPos2 = new Point3D();
            Point3D pPos = (Point3D)((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Position]).GetValueAtFrame(frame);
            Point3D pPoi = (Point3D)((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.LookAt]).GetValueAtFrame(frame);
            Point3D poiNorm;

            if (HasParentLayer && Properties[(int)PropertyTypes.LookAt].HasBoundProperty && Properties[(int)PropertyTypes.LookAt].BoundPropertyIgnoreParent)
            {
                Matrix3D parentMatrix = GetParentedTransformMatrixAtFrame(frame, false);
                pPos2 = parentMatrix.Transform(pPos);

                poiNorm = new Point3D(
                    pPoi.X - pPos2.X,
                    pPoi.Y - pPos2.Y,
                    pPoi.Z - pPos2.Z
                    );
            }
            else
            {
                poiNorm = new Point3D(
                    pPoi.X - pPos.X,
                    pPoi.Y - pPos.Y,
                    pPoi.Z - pPos.Z
                    );
            }


            double poiHeading = Math.Atan2(poiNorm.Y, poiNorm.X) - Math.PI / 2;
            double poiPitch = Math.Atan2(poiNorm.Z, Math.Sqrt(poiNorm.X * poiNorm.X + poiNorm.Y * poiNorm.Y));

            Matrix3D mTr = new Matrix3D();

            // Orientate
            Point3D orientation = (Point3D)((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Orientation]).GetValueAtFrame(frame);

            mTr.Rotate(new Quaternion(new Vector3D(0, 1, 0), orientation.Y));
            mTr.Rotate(new Quaternion(new Vector3D(0, 0, 1), orientation.Z));
            mTr.Rotate(new Quaternion(new Vector3D(1, 0, 0), orientation.X));

            // Look at rotation
            mTr.Rotate(new Quaternion(new Vector3D(0, 1, 0), (double)((CompLayerPropertyDouble)Properties[(int)PropertyTypes.Roll]).GetValueAtFrame(frame)));
            mTr.Rotate(new Quaternion(new Vector3D(1, 0, 0), poiPitch * MathHelper.RadDegMult));
            mTr.Rotate(new Quaternion(new Vector3D(0, 0, 1), poiHeading * MathHelper.RadDegMult));

            // Translate the matrix
            mTr.Translate(new Vector3D(pPos.X, pPos.Y, pPos.Z));

            return mTr;
        }

        public override void SetMeshLookAtPointAtFrame(int frame)
        {
            // Create look at vector
            LayerObjectMesh.LookAtPoint = new Point3D(0, GetLookAtDistance(frame), 0);
        }

        public double GetLookAtDistance(int frame)
        {
            Vector3D v = Point3D.Subtract(
                (Point3D)((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.LookAt]).GetValueAtFrame(frame),
                (Point3D)((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Position]).GetValueAtFrame(frame));
            return v.Length;
        }

        public override void SetMeshCenterPointAtFrame(int frame)
        {
        }

        public override void RemoveParentingJump(CompLayerBase parentLayer)
        {
            Matrix3D parentMatrix = parentLayer.GetParentedTransformMatrixAtFrame(0);
            Debug.WriteLine("Removing parent jump - {0},{1},{2}", parentMatrix.OffsetX, parentMatrix.OffsetY, parentMatrix.OffsetZ);

            // Translation offset
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Position]).CurrentXValue -= parentMatrix.OffsetX;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Position]).CurrentYValue -= parentMatrix.OffsetY;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Position]).CurrentZValue -= parentMatrix.OffsetZ;

            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.LookAt]).CurrentXValue -= parentMatrix.OffsetX;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.LookAt]).CurrentYValue -= parentMatrix.OffsetY;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.LookAt]).CurrentZValue -= parentMatrix.OffsetZ;

            // Orientation offset
            double pitch = Math.Atan2(-parentMatrix.M23, parentMatrix.M22);
            Matrix3D prePitch = Matrix3D.Multiply(parentMatrix, MathHelper.RotateXMatrix3D(pitch));

            double heading = Math.Atan2(prePitch.M21, prePitch.M22);
            Matrix3D preHeading = Matrix3D.Multiply(prePitch, MathHelper.RotateZMatrix3D(heading));

            double roll = Math.Atan2(preHeading.M13, preHeading.M11);

            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Orientation]).CurrentXValue -= pitch;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Orientation]).CurrentYValue -= roll;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Orientation]).CurrentZValue -= heading;
        }

        public override void AddParentingJump()
        {
            Matrix3D parentMatrix = ParentLayer.GetParentedTransformMatrixAtFrame(0);
            Debug.WriteLine("Adding parent jump - {0},{1},{2}", parentMatrix.OffsetX, parentMatrix.OffsetY, parentMatrix.OffsetZ);

            // Translation offset
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Position]).CurrentXValue += parentMatrix.OffsetX;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Position]).CurrentYValue += parentMatrix.OffsetY;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.Position]).CurrentZValue += parentMatrix.OffsetZ;

            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.LookAt]).CurrentXValue += parentMatrix.OffsetX;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.LookAt]).CurrentYValue += parentMatrix.OffsetY;
            ((CompLayerPropertyPoint3D)Properties[(int)PropertyTypes.LookAt]).CurrentZValue += parentMatrix.OffsetZ;

            // Orientation offset
        }
    }
}
